import * as THREE from "../../../../modules/three/three.module.js";
import Blocking from "../../model/Blocking.js";

// const quat = new THREE.Quaternion();
// const mat = new THREE.Matrix4();
const t1 = new THREE.Vector3();
const t2 = new THREE.Vector3();
const t3 = new THREE.Vector3();
const tx = new THREE.Vector3();
const ty = new THREE.Vector3();
const tz = new THREE.Vector3();

const cornersA = [];
const cornersB = [];
const cubePlanesA = [];
const cubePlanesB = [];

for (let i = 0; i < 8; i ++) {
  cornersA.push(new THREE.Vector3());
  cornersB.push(new THREE.Vector3());
  if (i < 6) {
    cubePlanesA.push(new THREE.Plane());
    cubePlanesB.push(new THREE.Plane());
  }
}
const blockingA = new Blocking();
const blockingB = new Blocking();

const epsilon = 0.01;
const epsilon2 = 0.001;

// 返回cube的六个外向平面
function cubePlanesFromCorners(corners, cubePlanes) {
  const [v0, v1, v2, v3, v4, v5, v6, v7] = corners;
  const [p013, p041, p074, p625, p657, p672] = cubePlanes;
  p013.setFromCoplanarPoints(v0, v1, v3);
  p041.setFromCoplanarPoints(v0, v4, v1);
  p074.setFromCoplanarPoints(v0, v7, v4);
  p625.setFromCoplanarPoints(v6, v2, v5);
  p657.setFromCoplanarPoints(v6, v5, v7);
  p672.setFromCoplanarPoints(v6, v7, v2);
  return cubePlanes;
}

export function segmentsFromCorners(corners) {
  const [v0, v1, v2, v3, v4, v5, v6, v7] = corners;
  const s01 = new THREE.Line3(v0, v1);
  const s12 = new THREE.Line3(v1, v2);
  const s23 = new THREE.Line3(v2, v3);
  const s30 = new THREE.Line3(v3, v0);
  const s45 = new THREE.Line3(v4, v5);
  const s56 = new THREE.Line3(v5, v6);
  const s67 = new THREE.Line3(v6, v7);
  const s74 = new THREE.Line3(v7, v4);
  const s40 = new THREE.Line3(v4, v0);
  const s51 = new THREE.Line3(v5, v1);
  const s62 = new THREE.Line3(v6, v2);
  const s73 = new THREE.Line3(v7, v3);
  return [s01, s12, s23, s30, s45, s56, s67, s74, s40, s51, s62, s73];
}

// const segmentsA = segmentsFromCorners(cornersA);
const segmentsB = segmentsFromCorners(cornersB);
const obbPts = [];
for (let i = 0; i < 15; i++) {
  obbPts.push(new THREE.Vector3());
}

function threeQuadPairsFromObb(obb) {
  obb.rotation.extractBasis(tx, ty, tz);
  const [hx,hy,hz, px1,px2,py1,py2,pz1,pz2, up, left] = obbPts;
  hx.copy(tx).multiplyScalar(obb.halfSize.x);
  hy.copy(ty).multiplyScalar(obb.halfSize.y);
  hz.copy(tz).multiplyScalar(obb.halfSize.z);
  px1.copy(obb.center).add(hx);
  px2.copy(obb.center).sub(hx);
  py1.copy(obb.center).add(hy);
  py2.copy(obb.center).sub(hy);
  pz1.copy(obb.center).add(hz);
  pz2.copy(obb.center).sub(hz);

  const [quadPairX, quadPairY, quadPairZ] = quadPairs;
  quadPairX.init(px1, px2, up.copy(px1).add(hy), left.copy(px1).add(hz));
  quadPairY.init(py1, py2, up.copy(py1).add(hz), left.copy(py1).add(hx));
  quadPairZ.init(pz1, pz2, up.copy(pz1).add(hy), left.copy(pz1).add(hx)); // pz1.clone().add(hy), pz1.clone().add(hx)
  return [quadPairX, quadPairY, quadPairZ];
}

class QuadPair {
  frame = new THREE.Matrix4();
  frameInv = new THREE.Matrix4();
  // quad a 的中心
  a = new THREE.Vector3();
  // quad b 的中心
  b = new THREE.Vector3();
  xAxis = new THREE.Vector3();
  yAxis = new THREE.Vector3();
  zAxis = new THREE.Vector3();
  hx = 0;
  hy = 0;
  bOffset = 0;

  aIntersected = false;
  bIntersected = false;
  aInSquare = false;
  bInSquare = false;
  ia = new THREE.Vector3();
  ib = new THREE.Vector3();

  init(pa, pb, ay, ax) {
    this.a.copy(pa);
    this.b.copy(pb);
    this.xAxis.copy(ax).sub(pa);
    this.hx = this.xAxis.length();
    this.xAxis.normalize();

    this.yAxis.copy(ay).sub(pa);
    this.hy = this.yAxis.length();
    this.yAxis.normalize();
    this.zAxis.copy(this.xAxis).cross(this.yAxis).normalize();
    this.frame.makeBasis(this.xAxis, this.yAxis, this.zAxis);
    this.frame.setPosition(pa);
    this.frameInv.copy(this.frame).invert();
    this.bOffset = pb.distanceTo(pa);
    const ab = t3.copy(pb).sub(pa);
    if (ab.dot(this.zAxis) < 0) {
      this.bOffset = -this.bOffset;
    }
  }

  _isInSquare(pt) {
    return pt.x < this.hx && pt.x > -this.hx
      && pt.y < this.hy && pt.y > -this.hy;
  }

  // 判断线段是否与矩形相交，线段正好和矩形的边界相交，不认为有交点
  intersectsWithLine3(line3) {
    t1.copy(line3.start).applyMatrix4(this.frameInv); // ?
    t2.copy(line3.end).applyMatrix4(this.frameInv);
    const zLen = Math.abs(t1.z - t2.z);
    this.aInSquare = false;
    this.bInSquare = false;
    if (zLen === 0) {
      // line3 和quad所在的平面平行 ？
      return;
    }

    if (t1.z * t2.z >= 0) {
      // 线段的起点 和 终点都位于 a平面的上面 或 下面；
      // t1.z * t2.z == 0的情况比较麻烦，表示线段的一个端点正好在平面上，现在作为 不相交的情况处理
      this.aIntersected = false;
    } else {
      this.aIntersected = true;
      const alpha = Math.abs(t1.z) / zLen;
      this.ia.copy(t1).lerp(t2, alpha);
      this.aInSquare = this._isInSquare(this.ia);
    }

    t1.z -= this.bOffset;
    t2.z -= this.bOffset;
    if (t1.z * t2.z >= 0) {
      // 线段的起点 和 终点都位于 b平面的上面 或 下面
      this.bIntersected = false;
    } else {
      this.bIntersected = true;
      const alpha = Math.abs(t1.z) / zLen;
      this.ib.copy(t1).lerp(t2, alpha);
      this.bInSquare = this._isInSquare(this.ib);
    }
  }
}

const quadPairs = [new QuadPair(), new QuadPair(), new QuadPair()];

/**
 * THREE.Box3 的intersectsBox方法，对于两个Box3 正好接触情况，总是返回true
 * 对于当前项目，两个Box3 正好接触时，一律返回false，认为两个Box3没有相撞，没有交集
 * @param {THREE.Box3} boxA
 * @param {THREE.Box3} boxB
 * @return {boolean}
 */
export function twoBoxesIntersected(boxA, boxB) {
  return !(boxA.max.x <= boxB.min.x + epsilon2 || boxA.min.x >= boxB.max.x - epsilon2 ||
    boxA.max.y <= boxB.min.y + epsilon2 || boxA.min.y >= boxB.max.y - epsilon2 ||
    boxA.max.z <= boxB.min.z + epsilon2 || boxA.min.z >= boxB.max.z - epsilon2);
}

/**
 * THREE 的OBB 的intersectsOBB方法，对于两个obb 正好接触情况，有时返回true，有时返回false
 * 对于当前项目，两个obb 正好接触时，一律返回false，认为两个obb没有相撞，没有交集
 * 思路：obbA的八个顶点都不在 obbB长方体的内部，并且obbB的八个顶点也都不在 obbA长方体的内部，
 * 则这两个obb没有相撞，没有交集
 * @param {OBB} obbA
 * @param {OBB} obbB
 * @return {boolean}
 */
export function twoObbsIntersected(obbA, obbB) {
  blockingA.initByObb(obbA).getCorners(cornersA);
  blockingB.initByObb(obbB).getCorners(cornersB);
  const aPlanes = cubePlanesFromCorners(cornersA, cubePlanesA);
  const bPlanes = cubePlanesFromCorners(cornersB, cubePlanesB);

  for (let i = 0; i < cornersB.length; i++) {
    const underPlanes = aPlanes.filter(plane => plane.distanceToPoint(cornersB[i]) < 0); // -minimum
    if (underPlanes.length === 6) { // B的某个顶点 在cube A的内部
      return true;
    }
  }

  for (let i = 0; i < cornersA.length; i++) {
    const underPlanes = bPlanes.filter(plane => plane.distanceToPoint(cornersA[i]) < 0); // -minimum
    if (underPlanes.length === 6) {
      return true;
    }
  }

  const quadPairs = threeQuadPairsFromObb(obbA);
  // segmentsFromCorners(obbB);
  for (let i = 0; i < segmentsB.length; i++) {
    const line3 = segmentsB[i];
    for (let j = 0; j < quadPairs.length; j++) {
      const pair = quadPairs[j];
      pair.intersectsWithLine3(line3);
      if (pair.aInSquare || pair.bInSquare) {
        // obbB 的十二条棱之一 穿过了 obbA的六个矩形网格之一
        return true;
      }
    }
  }

  return false;
}
